from concurrent.futures import ThreadPoolExecutor
from channels.generic.websocket import JsonWebsocketConsumer
import logging

logger = logging.getLogger()


"""
使用说明：
1.继承'WebSocketTransfer'类别
2.重写'event_list'列表，对事件进行注册
3.编写事件处理函数
范例如下：

def say_hello(content):  # 注意函数必须有content参数
    return "hello world"

class WebSocket(WebSocketTransfer):
    event_list = [
        event_case('SayHello', say_hello),
    ]

    def business_app_init(self):
        pass
"""


def event_case(event, execute):
    """
    使用示例
    event_router = [
        event_case('getListPorts', views.get_list_ports),
    ]
    """
    event = event.lower()
    return {
        'event': event,
        'execute': execute
    }


class WebSocketTransfer(JsonWebsocketConsumer):
    """
    WebSocket与业务应用的数据中转站
    Web发送过来的JSON数据协议如下
    {
        'id': '会话标识（可选项）',                      # 作为回话的唯一标识，若改字段存在，代表Web端需要该指令的应答
        'event': ‘事件（可选项）’                       # event为可选项，可于告知data所属的事件
        'dataType': 'bytes'|'string'|'json' (可选项)  # 标识数据的数据类型
        'data': '发送的数据(可选项)'                    # Web端发送过来的数据内容
    }
    返回给Web的JSON数据协议如下
    {
        'id': '会话标识' || 'event': '事件',           # 'id': 用于查询式应答，如Web端发送数据携带id，则返回给Web的数据也会携带id，作为回话的唯一标识
                                                     # 'event': 用于主动推送，告知data所属事件
        'dataType': 'string'|'json'|'array' (可选项)  # 标识数据的数据类型
        'data': '返回给Web的数据'                      # 返回给Web端发的数据内容
    }
    自带如下事件：
      接收事件
        'ping' 用于前段主动监测是否断开连接
           如果接收web端发来的如下格式数据
            {
                'id': '112233',
                'dataType': 'string',
                'event': 'ping'
            }
            服务器端数据响应如下
            {
                'id': '112233',
                'dataType': 'string',
                'data': 'pong'
            }
    """
    MAX_THREAD_WORKER = 5  # 最大使用线程数
    event_list = []        # 事件列表
    _thread_pool = None    # 线程池
    _client_list = []      # 客户端列表

    def __init__(self, *args, **kwargs):
        if not WebSocketTransfer._thread_pool:
            self.init()
            WebSocketTransfer._thread_pool = ThreadPoolExecutor(max_workers=WebSocketTransfer.MAX_THREAD_WORKER)
        super().__init__(*args, **kwargs)

    def connect(self):
        self.accept()
        WebSocketTransfer._client_list.append(self)
        logger.info('服务器：新客户端连接，现共有%d个客户端' % len(WebSocketTransfer._client_list))

    def disconnect(self, close_code):
        self._client_list.remove(self)
        logger.info('服务器：客户端断开连接，现共有%d个客户端' % len(WebSocketTransfer._client_list))

    def _event_worker(self, content):
        id = content.get('id', None)
        event = content.get('event', None)
        event = event.lower() if event else None
        data_type = content.get('dataType', None)
        if not data_type:
            content['dataType'] = 'string'
        logger.debug('接收数据: %s', content)
        if id and event == 'ping':
            self.response_data_send('pong', id)
        else:
            found = False
            for e in self.event_list:
                if e['event'] == event:
                    found = True
                    try:
                        response_data = e['execute'](content)
                        if response_data and id:
                            self.response_data_send(response_data, id, data_type=data_type)
                    except Exception as e:
                        logger.error(str(e))
                    break
            if not found:
                logger.warning("事件【%s】未注册" % event)

    def receive_json(self, content, **kwargs):
        # Thread(target=self._event_worker, args=(content,), daemon=True).start()
        future = WebSocketTransfer._thread_pool.submit(self._event_worker, content)
        future.add_done_callback(lambda future: None)  # 添加回调函数，并启动线程

    def init(self):
        """
        用于执行只需初始化一次的内容，若在__init__中初始化，则会每当一个Web客户端连接时都会执行
        """
        pass

    @staticmethod
    def _generate_send_data(data=None, id=None, event=None, data_type='string'):
        send_data = {}
        if id and event:
            send_data['event'] = event
        elif id:
            send_data['id'] = id
        elif event:
            send_data['event'] = event
        if data:
            if isinstance(data, bytearray) or isinstance(data, bytes):
                send_data['data'] = data.decode('utf8')
            else:
                send_data['data'] = data
        if data_type:
            send_data['dataType'] = data_type

        return send_data

    def response_data_send(self, data=None, id=None, event=None, data_type='string'):
        """
        该函数主要用于户客户端指令的应答
        """
        send_data = self._generate_send_data(data, id, event, data_type)
        # if send_data['data'] != 'pong':
        logger.debug('响应数据: %s', send_data)
        self.send_json(send_data)

    @classmethod
    def push_data_send(cls, data, event=None, data_type='string'):
        """
        该函数主要用于服务器数据的主动推送
        """
        send_data = cls._generate_send_data(data, None, event, data_type)
        logger.debug('推送数据: %s', send_data)
        for client in cls._client_list:
            client.send_json(send_data)
